/*
 * AnalysisOps.java
 *
 * Created on February 19, 2007, 11:22 AM
 *
 * Copyright 2008 David D. Emory
 * 
 * This file is part of Five Points. See <http://www.fpdev.org> for
 * additional information regarding the project.
 * 
 * Five Points is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Five Points is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Five Points.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.fpdev.apps.rtemaster.analysis;

import org.fpdev.apps.rtemaster.*;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.*;
import org.fpdev.core.FPEngine;
import org.fpdev.core.algo.path.Pathfinder;
import org.fpdev.core.algo.DepthLimitedSearch;
import org.fpdev.core.algo.RouteDLS;
import org.fpdev.core.basenet.*;
import org.fpdev.core.transit.*;
import org.fpdev.core.trip.*;
import org.fpdev.core.trip.NonMotorizedSubtrip;
import org.fpdev.apps.rtemaster.gui.RMGUI;
import org.fpdev.apps.rtemaster.gui.TopoGraphPanel;
import org.fpdev.apps.rtemaster.gui.map.HighlightedPoint;
import org.fpdev.apps.rtemaster.gui.map.MapDrawItems;
import org.fpdev.util.FPUtil;
import org.fpdev.util.gui.GUIFactory;
import org.fpdev.util.gui.UneditableTableModel;

/**
 *
 * @author demory
 */
public class AnalysisOps {

  private FPEngine engine_;
  private RouteMaster ac_;

  private Visualizer visualizer_;

  private List<SubRoute> subs_; // used for basic node/link queries
  private List<SubRouteInfo> sris_; // used for departing/arriving facilities queries

  private int listMode_;
  private BNode activeNode_;
  private RecentTripQueries recentQueries_;
  //private Animation animation_;
  private BikeTripItem activeBikeTrip_;

  public static final int RESOLVE_SUCCESS = 1;
  public static final int RESOLVE_FAILURE = 2;
  public static final int RESOLVE_CANCEL = 3;
  public static final int LISTMODE_QNODE = 1;
  public static final int LISTMODE_QLINK = 2;
  public static final int LISTMODE_ARRFACS = 3;
  public static final int LISTMODE_DEPFACS = 4;
  public static final int LISTMODE_DIJKSTRA = 5;

  /** Creates a new instance of CAnalysisManager */
  public AnalysisOps(RouteMaster av) {
    ac_ = av;
    engine_ = ac_.getEngine();
    listMode_ = 0;
    visualizer_ = new Visualizer(ac_);
    recentQueries_ = new RecentTripQueries(10);
  }

  public Visualizer getVisualizer() {
    return visualizer_;
  }

  public static boolean isAnalysisEvent(RMEvent e) {
    return e.getType() / 100 % 10 == 3; // true if type id = X3XX
  }

  public void handleAnalysisEvent(RMEvent ev) {
    BNode node = ev.getNodeProperty("node");
    BLink link = ev.getLinkProperty("link");

    switch(ev.getType()) {
      case EventTypes.MCLICK_AN_TRIP_START:
        if(node == null) return;
        setStartLocation(new LocationIsect(node, ac_.getEngine().getBaseNet()));
        ac_.getGUI().getMapPanel().refresh(false, false, true);
        break;
      case EventTypes.MCLICK_AN_TRIP_END:
        if(node == null) return;
        setEndLocation(new LocationIsect(node, ac_.getEngine().getBaseNet()));
        ac_.getGUI().getMapPanel().refresh(false, false, true);
        break;
      case EventTypes.MCLICK_AN_QUERY_LINK:
        if(link == null) return;
        queryLinkForService(link);
        break;
      case EventTypes.MCLICK_AN_QUERY_NODE:
        if(node == null) return;
        queryNodeForService(node);
        break;
      case EventTypes.ANPANEL_SNAPSHOT_REFRESH:
        //CalendarService service = (CalendarService) ev.getProperty("service");
        int stime = ev.getIntegerProperty("stime");
        visualizer_.refreshSnapshot(stime);
        break;
    }

  }
  public RecentTripQueries getRecentTripQueries() {
    return recentQueries_;
  }
  
  public BikeTripItem getActiveBikeTripItem() {
    return activeBikeTrip_;
  } 
  
  // QUERY LINK/NODE FOR SERVICE
  public void queryLinkForService(BLink link) {
    //av_.msg("Querying link #" + link.getID());

    subs_ = new LinkedList<SubRoute>();

    for(Path path : link.getRegisteredPaths())
      if(path.isTransitPath()) subs_.add(((TransitPath)path).getSubRoute());

    if(subs_.size() == 0) ac_.msg("No routes found at link "+link.getID());
    else showSubRouteTable();
  }

  public void queryNodeForService(BNode node) {
    ac_.msg("Querying node #" + node.getID());

    subs_ = new LinkedList<SubRoute>();

    for(BLink link : ac_.getEngine().getBaseNet().incidentLinks(node))
      for(Path path : link.getRegisteredPaths())
        if(path.isTransitPath()) subs_.add(((TransitPath)path).getSubRoute());

    if(subs_.size() == 0) ac_.msg("No routes found at node "+node.getID());
    else showSubRouteTable();
  }

  private void showSubRouteTable() {
    Collections.sort(subs_);

    if(subs_.size() > 0) {
      ac_.getGUI().getLowerPanel().initPanel(false);
      ac_.getGUI().showLowerPanel(100);
      String[] colNames = new String[4];
      colNames[0] = "Scenario";
      colNames[1] = "Provider";
      colNames[2] = "ID";
      colNames[3] = "Route Name";

      String[][] table = new String[subs_.size()][4];
      int r = 0;
      for(SubRoute sub : subs_) {
        table[r][0] = sub.getRoute().getScenario().getName();
        table[r][1] = sub.getRoute().getProvider().getName();
        table[r][2] = sub.getFullID();
        table[r][3] = sub.getRoute().getName();
        r++;
      }
      createListTable(new UneditableTableModel(table, colNames));
    } else {
      ac_.getGUI().getLowerPanel().clearTable();
      ac_.getGUI().hideLowerPanel();
    }
    listMode_ = AnalysisOps.LISTMODE_QLINK;
  }

  private RouteDLS dls_;

  public void showDepartingFacilities(BNode node) {
    ac_.msg("Scanning for departing facilities...");
    showNearbyFacilities(node, RouteDLS.MODE_DEPARTING);
    listMode_ = AnalysisOps.LISTMODE_DEPFACS;
  }

  public void showArrivingFacilities(BNode node) {
    ac_.msg("Scanning for arriving facilities...");
    showNearbyFacilities(node, RouteDLS.MODE_ARRIVING);
    listMode_ = AnalysisOps.LISTMODE_ARRFACS;
  }

  public void showNearbyFacilities(BNode node, int mode) {
    //av_.getGUI().getMapPanel().clearFocusNodes();
    /*bfs_ = null;//new CRouteBFS(av_.getEngine(), (int) av_.getGUI().getAnalysisOptionsPanel().getTripPlannerPanel().getMaxWalkDistFt(), mode);
    bfs_.execute(node);
    Iterator<BNode> visited = bfs_.visitedNodes();

    while (visited.hasNext()) {
      BNode n = visited.next();
    //cv_.msg("node: "+n.getID());
    //av_.getGUI().getMapPanel().addActionNode(n);
    }
    //av_.getGUI().getMapPanel().addHighlightNode(node);
    av_.getGUI().getMapPanel().updated(false, false, true);
    av_.getGUI().getMapPanel().repaint();

    // create route display list 
    Map<String, SubRouteInfo> sriLookup = ((RouteDLS) bfs_).getSRILookup();*/
    sris_ = new ArrayList<SubRouteInfo>();
    dls_ = new RouteDLS(ac_.getEngine(), DepthLimitedSearch.TYPE_WALK, (int) ac_.getGUI().getAnalysisOptionsPanel().getTripPlannerPanel().getMaxWalkDistFt(), mode, 0);
    ac_.getEngine().getBaseNet().getGraph().setWeightMode(BaseNetGraph.WEIGHT_WALK, ac_.getActiveScenario());
    dls_.execute(node);
    
    Map<String, SubRouteInfo> sriLookup = dls_.getSRILookup();


    //System.out.println("found "+sriLookup.size());
    if (sriLookup.size() > 0) {
      ac_.getGUI().getLowerPanel().initPanel(false);
      ac_.getGUI().showLowerPanel();
      String[] colNames = new String[5];
      colNames[0] = "ID";
      colNames[1] = "Route Name";
      colNames[2] = "Access Location";
      colNames[3] = "Distance";
      colNames[4] = "DLI Info";


      String[][] table = new String[sriLookup.size()][5];
      Iterator<SubRouteInfo> sris = sriLookup.values().iterator();
      int r = 0;
      while (sris.hasNext()) {
        SubRouteInfo sri = sris.next();
        sris_.add(sri);
        table[r][0] = sri.getSubRoute().getFullID();
        table[r][1] = sri.getSubRoute().getRoute().getName();
        table[r][2] = sri.getAccessNode().getDisplayName(ac_.getEngine().getBaseNet());
        table[r][3] = FPUtil.distanceStr((int) sri.getDistance());
        table[r][4] = (sri.getDLI()) + ", " + sri.getSubRoute().getRouteLinkFromDLI(sri.getDLI()).getID();
        r++;
      }
      createListTable(new UneditableTableModel(table, colNames));
    } else {
      ac_.getGUI().getLowerPanel().clearTable();
      ac_.getGUI().hideLowerPanel();
    }
    activeNode_ = node;
  }

  private List<TripItem> tripItems_;
  private Location startLoc_ = null,  endLoc_ = null;

  public Location getStartLocation() {
    return startLoc_;
  }

  public Location getEndLocation() {
    return endLoc_;
  }

  public void setStartLocation(Location loc) {
    startLoc_ = loc;
    if (loc == null) {
      return;
    }
    ac_.getGUI().getAnalysisOptionsPanel().getTripPlannerPanel().setStartText(loc.getDispText(false));
    ac_.getGUI().getMapPanel().getDrawItems().removeItem(MapDrawItems.BEST_TRIP);
    ac_.getGUI().getMapPanel().getDrawItems().addItem(MapDrawItems.BEST_TRIP_START, new HighlightedPoint(loc.getNode(), new Color(0, 127, 0), 8), "a", 5);
  }

  public void setEndLocation(Location loc) {
    endLoc_ = loc;
    if (loc == null) {
      return;
    }
    ac_.getGUI().getAnalysisOptionsPanel().getTripPlannerPanel().setEndText(loc.getDispText(false));
    ac_.getGUI().getMapPanel().getDrawItems().removeItem(MapDrawItems.BEST_TRIP);
    ac_.getGUI().getMapPanel().getDrawItems().addItem(MapDrawItems.BEST_TRIP_END, new HighlightedPoint(loc.getNode(), Color.red, 8), "a", 5);
  }

  public int resolveStart(String input) {
    startLoc_ = null;
    List<Location> locs = engine_.getLocations().resolveLocationFromInput(input);
    if (locs.size() == 0) {
      return RESOLVE_FAILURE;
    }
    if (locs.size() == 1) {
      setStartLocation(locs.get(0));
      ac_.getGUI().getMapPanel().refresh(false, false, true);
      return RESOLVE_SUCCESS;
    }
    List<String> strList = new LinkedList<String>();
    Iterator<Location> locsIter = locs.iterator();
    while (locsIter.hasNext()) {
      strList.add(locsIter.next().getDispText(false));
    }
    int selLoc = GUIFactory.showSelectItemDialog(ac_.getGUI(), strList.iterator());
    if (selLoc < 0) {
      return RESOLVE_CANCEL;
    }
    setStartLocation(locs.get(selLoc));
    ac_.getGUI().getMapPanel().refresh(false, false, true);
    return RESOLVE_SUCCESS;
  }

  public int resolveEnd(String input) {
    endLoc_ = null;
    List<Location> locs = engine_.getLocations().resolveLocationFromInput(input);
    if (locs.size() == 0) {
      return RESOLVE_FAILURE;
    }
    if (locs.size() == 1) {
      setEndLocation(locs.get(0));
      ac_.getGUI().getMapPanel().refresh(false, false, true);
      return RESOLVE_SUCCESS;
    }
    List<String> strList = new LinkedList<String>();
    Iterator<Location> locsIter = locs.iterator();
    while (locsIter.hasNext()) {
      strList.add(locsIter.next().getDispText(false));
    }
    int selLoc = GUIFactory.showSelectItemDialog(ac_.getGUI(), strList.iterator());
    if (selLoc < 0) {
      return RESOLVE_CANCEL;
    }
    setEndLocation(locs.get(selLoc));
    ac_.getGUI().getMapPanel().refresh(false, false, true);
    return RESOLVE_SUCCESS;
  }

  public Trip findBestTrip() {
    RMGUI gui = ac_.getGUI();

    activeBikeTrip_ = null;
    if (startLoc_ == null) {
      ac_.msg("Invalid start location");
      return null;
    }
    if (endLoc_ == null) {
      ac_.msg("Invalid end location");
      return null;
    }
    CalendarService service = gui.getAnalysisOptionsPanel().getTripPlannerPanel().getService();
    if(service == null) {
      ac_.msg("Invalid calendar specifier");
      return null;
    }
    System.out.println("service: "+service.getName());

    ac_.getGUI().getAnalysisOptionsPanel().getTripPlannerPanel().checkStartLocation(startLoc_);
    ac_.getGUI().getAnalysisOptionsPanel().getTripPlannerPanel().checkEndLocation(endLoc_);

    int tripType = gui.getAnalysisOptionsPanel().getTripPlannerPanel().getTripType();
    int time = gui.getAnalysisOptionsPanel().getTripPlannerPanel().getSTime();
    Pathfinder.TimeMode timeMode = gui.getAnalysisOptionsPanel().getTripPlannerPanel().getTimeMode();
    int optMode = gui.getAnalysisOptionsPanel().getTripPlannerPanel().getOptMode();
    double walkSpeed = gui.getAnalysisOptionsPanel().getTripPlannerPanel().getWalkSpeedFPS();
    double bikeTypeFactor = gui.getAnalysisOptionsPanel().getTripPlannerPanel().getBikeTypeFactor();
    double bikeTopFactor = gui.getAnalysisOptionsPanel().getTripPlannerPanel().getBikeTopFactor();
    BNode start = startLoc_.getNode();
    BNode end = endLoc_.getNode();

    int walkLimit = (int) gui.getAnalysisOptionsPanel().getTripPlannerPanel().getMaxWalkDistFt();
    ac_.msg((timeMode == Pathfinder.TimeMode.DEPART_AT ? "Start" : "End") + " time: " + FPUtil.sTimeToStr(time));
    boolean success = ac_.getEngine().getPathfinder().findShortestTrip(ac_.getActiveScenario(), tripType, time, service, timeMode, walkSpeed, walkLimit, bikeTypeFactor, bikeTopFactor, start, end);

    Trip trip = null;
    if (success) {
      if (tripType == Pathfinder.WALK_ONLY || tripType == Pathfinder.BIKE_ONLY) {
        trip = ac_.getEngine().getPathfinder().getBestTripNM();
      } else {
        trip = (optMode == Pathfinder.TRAVEL_TIME) ? ac_.getEngine().getPathfinder().getBestTripTT() : ac_.getEngine().getPathfinder().getBestTripNT();
      }
      if (tripType == Pathfinder.WALK_TO_TRANSIT || tripType == Pathfinder.BIKE_TO_TRANSIT) {
        engine_.getRouteNetwork().setDrawPath(ac_.getEngine().getPathfinder().getRouteDijkstraLinks());
      }
      tripItems_ = new ArrayList<TripItem>();

      // add path to map
      ac_.getGUI().getMapPanel().getDrawItems().addItem(MapDrawItems.BEST_TRIP, trip, "a");

      // if bike trip, render elevation graph
      if (tripType == Pathfinder.BIKE_ONLY) {
        BikeTripItem bikeTrip = (BikeTripItem) trip.items().next();
        ac_.msg("elev range: " + bikeTrip.getMinElev() + " to " + bikeTrip.getMaxElev());
        renderBikeElevGraph(bikeTrip);
      }

      // init itinerary table
      DefaultTableModel model = createTable(trip, tripType);
      JTable jtable = createListTable(model);
      jtable.setIntercellSpacing(new java.awt.Dimension(3, 5));
      jtable.setShowVerticalLines(false);
      int rowHeight = 18;
      jtable.setRowHeight(rowHeight);
      jtable.setDefaultRenderer(Object.class, new TripCellRenderer());
      listMode_ = LISTMODE_DIJKSTRA;

      // make itinerary table visible
      //av_.getGUI().getMapPanel().setSupressRedrawOnResize();
      ac_.getGUI().getMapPanel().setResizeToDrawable(MapDrawItems.BEST_TRIP);
      ac_.getGUI().getLowerPanel().initPanel(tripType == Pathfinder.BIKE_ONLY);
      int rowCount = trip.subItemCount() + 1;
      if (tripType == Pathfinder.BIKE_ONLY && rowCount < 10) {
        rowCount = 10;
      }
      ac_.getGUI().showLowerPanel((rowCount > 12 ? 12 : rowCount) * rowHeight);//tripType == Pathfinder.BIKE_ONLY ? 235 : 150);
      ac_.getGUI().getLowerPanel().setDividerLocation(0.5);
      ac_.getGUI().getLowerPanel().setTable(jtable);
      ac_.getGUI().getMapPanel().updated(false, false, true);
    //av_.getGUI().zoomToTrip(trip);
    //av_.getGUI().getMapPanel().zoomDrawable(MapDrawItems.BEST_TRIP, 0.1);


    //TripPlannerOptionsPanel pan = av_.getGUI().getAnalysisOptionsPanel().getTripPlannerPanel();
    //recentQueries_.addQuery();
    } else { // no trip found
      Iterator<String> msgs = engine_.getPathfinder().getMessages();
      while (msgs.hasNext()) {
        ac_.msg("Pathfinder msg: " + msgs.next());
      }
    }
    if(ac_.getEngine().getDB() != null)
      ac_.getEngine().getDataPackage().getCoreDB().logTripQuery(1, 0, ac_.getActiveScenario().getID(), tripType, startLoc_.getDispText(false), endLoc_.getDispText(false), time, service.getID(), timeMode.getNumCode(), walkSpeed, walkLimit, bikeTypeFactor, bikeTopFactor, success ? 1 : 0);
    return trip;
  }

  private DefaultTableModel createTable(Trip trip, int tripType) {
    String[] colNames;
    String[][] table;

    if (tripType == Pathfinder.BIKE_ONLY) {
      colNames = new String[4];
      colNames[0] = "Facility";
      colNames[1] = "From";
      colNames[2] = "To";
      colNames[3] = "Elapsed";

      table = new String[trip.subItemCount()][4];
      Iterator<TripItem> items = trip.items();
      int r = 0;
      while (items.hasNext()) {
        TripItem item = items.next();
        //System.out.println(""+item.getType());
        if (item.getType() == TripItem.TRIP_BIKE) {
          for(NonMotorizedSubtrip subtrip : ((NonMotorizedTripItem) item).subtrips()) {
            table[r][0] = subtrip.getName();
            table[r][1] = subtrip.getFrom(engine_.getBaseNet());
            table[r][2] = subtrip.getTo(engine_.getBaseNet());
            table[r][3] = "" + FPUtil.elapsedTimeStr(subtrip.getEnd() - subtrip.getStart());
            r++;
            tripItems_.add(subtrip);
          }
        }
      }
    } else {
      colNames = new String[7];
      colNames[0] = "Mode";
      colNames[1] = "Facility";
      colNames[2] = "From";
      colNames[3] = "To";
      colNames[4] = "Start";
      colNames[5] = "End";
      colNames[6] = "Elapsed";

      table = new String[trip.subItemCount()][7];
      Iterator<TripItem> items = trip.items();
      int r = 0;
      while (items.hasNext()) {
        TripItem item = items.next();
        //System.out.println(""+item.getType());
        switch (item.getType()) {
          case TripItem.TRIP_WALK:
          case TripItem.TRIP_BIKE:
            for(NonMotorizedSubtrip subtrip : ((NonMotorizedTripItem) item).subtrips()) {
              table[r][0] = (item.getType() == TripItem.TRIP_WALK) ? "Walk" : "Bike";
              table[r][1] = subtrip.getName();
              //table[r][2] = "-";
              table[r][2] = subtrip.getFrom(engine_.getBaseNet());
              table[r][3] = subtrip.getTo(engine_.getBaseNet());
              table[r][4] = "" + FPUtil.sTimeToStr(subtrip.getStart());
              table[r][5] = "" + FPUtil.sTimeToStr(subtrip.getEnd());
              table[r][6] = "" + FPUtil.elapsedTimeStr(subtrip.getEnd() - subtrip.getStart());
              r++;
              tripItems_.add(subtrip);
            }
            break;
          case TripItem.TRIP_TRANSIT:
            TransitTripItem transitTrip = (TransitTripItem) item;
            table[r][0] = transitTrip.getSubRoute().getPath().getType().getName();
            /*switch(transitTrip.getSubRoute().getPath().getType()) {
            case TransitPath.METRO: table[r][0] = "Heavy Rail"; break;
            case TransitPath.BUS: table[r][0] = "Local Bus"; break;
            case TransitPath.LIGHTRAIL: table[r][0] = "Light Rail"; break;
            case TransitPath.REGIONALRAIL: table[r][0] = "Regional Rail"; break;
            default: table[r][0] = "Transit";
            }*/

            table[r][1] = transitTrip.getSubRoute().getFullID();
            table[r][2] = transitTrip.getFrom(engine_.getBaseNet());
            table[r][3] = transitTrip.getTo(engine_.getBaseNet());
            table[r][4] = "" + FPUtil.sTimeToStr(transitTrip.getStart());
            table[r][5] = "" + FPUtil.sTimeToStr(transitTrip.getEnd());
            table[r][6] = "" + FPUtil.elapsedTimeStr(transitTrip.getEnd() - transitTrip.getStart());
            r++;
            tripItems_.add(transitTrip);
            break;
          case TripItem.TRIP_WAIT:
            WaitItem waitItem = (WaitItem) item;
            table[r][0] = "Wait";
            table[r][1] = waitItem.getLocation().getDisplayName(engine_.getBaseNet());
            //table[r][2] = "-";
            table[r][2] = "-";
            table[r][3] = "-";
            table[r][4] = "" + FPUtil.sTimeToStr(waitItem.getStart());
            table[r][5] = "" + FPUtil.sTimeToStr(waitItem.getEnd());
            table[r][6] = "" + FPUtil.elapsedTimeStr(waitItem.getEnd() - waitItem.getStart());
            r++;
            tripItems_.add(waitItem);
            break;
          case TripItem.TRIP_NAVIGATE:
            NavigateItem navItem = (NavigateItem) item;
            table[r][0] = "Navigate";
            table[r][1] = navItem.getStation().getName();
            //table[r][2] = "-";
            table[r][2] = "-";
            table[r][3] = "-";
            table[r][4] = "" + navItem.getStart();
            table[r][5] = "" + navItem.getEnd();
            table[r][6] = "" + (navItem.getEnd() - navItem.getStart());
            r++;
            tripItems_.add(navItem);
            break;
        }
      }
    }

    return new UneditableTableModel(table, colNames);
  }

  public void renderBikeElevGraph(BikeTripItem trip) {
    activeBikeTrip_ = trip;
    renderBikeElevGraph();
  }

  public void renderBikeElevGraph() {
    if (activeBikeTrip_ == null) {
      return;
    }
    int dpHeight = ac_.getGUI().getLowerPanel().getLastHeight();
    System.out.println("dpHeight = " + dpHeight);
    BufferedImage elev = activeBikeTrip_.getElevGraph(ac_.getEngine(), dpHeight - 24);
    //av_.getGUI().setDetailsGraphic(elev);
    ac_.getGUI().getLowerPanel().setLeftPanel(new TopoGraphPanel(elev, 40, dpHeight - 24, ac_));
  }

  private class TripCellRenderer extends DefaultTableCellRenderer {

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focused, int row, int column) {
      super.getTableCellRendererComponent(table, value, selected, focused, row, column);

      // Set the background color
      Color bg = Color.white, fg = Color.black;
      if (column == 0) {
        TripItem item = (TripItem) tripItems_.get(row);
        switch (item.getType()) {
          case TripItem.TRIP_BIKE:
          case TripItem.TRIP_BIKESUB:
            bg = Color.green;
            break;
          case TripItem.TRIP_WALK:
          case TripItem.TRIP_WALKSUB:
            bg = selected ? Color.green : new Color(0, 127, 0);
            fg = selected ? Color.black : Color.white;
            break;
          case TripItem.TRIP_WAIT:
            bg = selected ? Color.gray : Color.black;
            fg = Color.white;
            break;
          case TripItem.TRIP_TRANSIT:
            switch (((TransitTripItem) item).getSubRoute().getPath().getType()) {
              case METRO:
                bg = selected ? Color.red : new Color(195, 0, 0);
                fg = Color.white;
                break;
              case LIGHTRAIL:
                bg = Color.orange;
                fg = Color.black;
                break;
              case REGIONALRAIL:
                bg = Color.magenta;
                fg = Color.black;
                break;
              case BUS:
                bg = selected ? Color.blue : new Color(0, 0, 127);
                fg = Color.white;
                break;

            }
            break;


        }
        this.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 11));
      }
      setBackground(bg);
      setForeground(fg);

      return this;
    }
  }

  ////////////////////// LIST TABLE METHODS ///////////////////////////

  public JTable createListTable(TableModel data) {
    final JTable table = new JTable();

    table.setModel(data);

    // initialize row selection model
    table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    table.setShowVerticalLines(false);
    FPUtil.setPreferredColumnWidths(table, new double[]{0.2, 0.2, 0.2, 0.4});

    table.addMouseListener(new MouseAdapter() {

      @Override
      public void mouseClicked(MouseEvent e) {
        if (e.getClickCount() >= 2) {
          int row = table.rowAtPoint(e.getPoint());
          //av_.msg("d-click row " + row);
          listRowDoubleClicked(row);
        }
      }
    });

    final ListSelectionModel rowLSM = table.getSelectionModel();
    rowLSM.addListSelectionListener(new ListSelectionListener() {

      public void valueChanged(ListSelectionEvent e) {
        if (e.getValueIsAdjusting()) {
          return;
        } //Ignore extra messages.
        //ListSelectionModel mainLSM = (ListSelectionModel) e.getSource();
        if (!rowLSM.isSelectionEmpty()) {
          int selectedRow = rowLSM.getMinSelectionIndex();
          listRowSelected(selectedRow);
        }
      }
    });
    ac_.getGUI().getLowerPanel().setTable(table);
    ac_.getGUI().getLowerPanel().repaintTable();
    return table;
  }

  public void listRowSelected(int row) {
    ac_.msg("table row selected: " + row);
    if (listMode_ == LISTMODE_QNODE || listMode_ == LISTMODE_QLINK) {
      ac_.getGUI().getMapPanel().getDrawItems().addItem(MapDrawItems.AN_TRANSIT_PATH, subs_.get(row).getPath(), "a");
    }

    if (listMode_ == LISTMODE_ARRFACS || listMode_ == LISTMODE_DEPFACS) {
      SubRouteInfo sri = sris_.get(row);
      ac_.getGUI().getMapPanel().getDrawItems().addItem(MapDrawItems.AN_TRANSIT_PATH, sri.getSubRoute().getPath(), "a");
      Path accessPath = new Path(ac_.getEngine());
      System.out.println("creating accpath");
      BNode curNode = sri.getAccessNode();
      while (curNode != activeNode_) {
        BLink link = dls_.getParentLink(curNode);
        System.out.println(" link: "+link.getID());
        if (listMode_ ==  LISTMODE_ARRFACS) {
          accessPath.addToStart(link);
        }
        if (listMode_ == LISTMODE_DEPFACS) {
          accessPath.addToEnd(link);
        }
        curNode = engine_.getBaseNet().opposite(curNode, link);
      }
      accessPath.setDefaultDrawColor(Color.blue);
      accessPath.setDefaultDrawWidth(2);
      System.out.println("accpath len="+accessPath.linkCount());
      ac_.getGUI().getMapPanel().getDrawItems().addItem(MapDrawItems.AN_ACCESS_PATH, accessPath, "a"); //setActionPath(accessPath);
    }
    ac_.getGUI().getMapPanel().refresh(false, false, true);
  }

  private void listRowDoubleClicked(int row) {
    if (listMode_ == LISTMODE_QNODE || listMode_ == LISTMODE_QLINK) {
      SubRoute sub = subs_.get(row);
      ac_.getGUI().setMode(RMGUI.Mode.ROUTE);
      ac_.getGUI().getRouteSelectionPanel().selectProvider(sub.getRoute().getProvider());
      ac_.getGUI().getRouteSelectionPanel().selectRouteByID(sub.getRoute().getID());
      ac_.getGUI().getRouteSelectionPanel().selectSub(sub);
    }
  }
}
